/**
 * Copyright (c) 2011-2012
 * OmniCore CodeGuide
 */

package de.tum.in.oose.bumpers.control;

import java.applet.AudioClip;
import java.util.ArrayList;

import javax.swing.JOptionPane;

import de.tum.in.oose.bumpers.cars.Car;
import de.tum.in.oose.bumpers.ui.GameBoard;
import de.tum.in.oose.bumpers.ui.ToolBar;
import de.tum.in.softwareengineering1.ws201112.team11.gameframework.base.BaseGame;
import de.tum.in.softwareengineering1.ws201112.team11.gameframework.base.BaseOption;
import de.tum.in.softwareengineering1.ws201112.team11.gameframework.exception.GameException;
import de.tum.in.softwareengineering1.ws201112.team11.gameframework.model.OptionType;

/**
 * Referee.
 */
public final class Referee extends BaseGame implements Runnable {
	
	private static AudioClip music;
    private static AudioClip bang;
	private static int sleepTime = 200;
	private static Referee instance;
	private Thread theThread;
    private boolean isRunning;
    private boolean isPaused;
    
    private String test;
	
	/**
	 * @return the music
	 */
	public static AudioClip getMusic() {
		return music;
	}



	/**
	 * @param music the music to set
	 */
	public static void setMusic(AudioClip music) {
		Referee.music = music;
	}



	/**
	 * @return the bang
	 */
	public static AudioClip getBang() {
		return bang;
	}



	/**
	 * @param bang the bang to set
	 */
	public static void setBang(AudioClip bang) {
		Referee.bang = bang;
	}



	/**
	 * @return the test
	 */
	public String getTest() {
		return test;
	}



	/**
	 * @param test the test to set
	 */
	public void setTest(String test) {
		this.test = test;
	}

	private CollisionStrategy collisionStrategy = new DefaultCollision();
	
	private Referee(){
		setFramesPerSecond(15);
		fillInOptions();
	}
	
	
	/**
	 * @return referee singleton
	 */
	public static Referee getInstance(){
		if(instance == null){
			instance = new Referee();
		}
		return instance;
	}
	
	/**
	 * inits the game.
	 */
	public void initGame() {
		GameBoard.getInstance().initiate();
		ToolBar.getInstance().enableButtons();
	}
	
	/**
	 * starts the game.
	 * 
	 * @return whether the action has been successful
	 */
	public Boolean startGame() {
		if(isRunning){ return Boolean.FALSE;}
		music.loop();
		isRunning = true;
		isPaused = false;
		theThread = new Thread(this);
		theThread.start();
		ToolBar.getInstance().enableButtons();
		
		return Boolean.TRUE;
	}
	
	/**
	 * stops the game.
	 * 
	 * @return whether the action has been successful
	 */
    public Boolean endGame() {
		if(!isRunning){ return Boolean.FALSE;}
		music.stop();
		isRunning = false;
		ToolBar.getInstance().enableButtons();
		
		return Boolean.TRUE;
    }
    
    /**
     * pause or unpause the game.
     * 
     * @return whether the action has been successful
     */
    public Boolean pauseGame() {
    	if (!isPaused) {
    		isPaused = true;
    		return endGame();
    	}
    	else {
    		isPaused = false;
    		return startGame();
    	}
    }
	
	/**
	 * run method for threading.
	 */
	public void run() {
		while (isRunning) {
			try {
				Thread.sleep(sleepTime);
			}
			catch (InterruptedException e) { e.printStackTrace(); }
			moveCars();
		}
	}
	
	/**
	 * move the cars and update position and ui.
	 */
	public void moveCars() {
		GameBoard playingField = GameBoard.getInstance();
		Car[] cars = playingField.getRobotCars();
		
		int maxX = playingField.getSize().width;
		int maxY = playingField.getSize().height;
		for (int i = 0; i < cars.length; i++) {
			cars[i].updatePosition(maxX, maxY);
		}
		playingField.getDrivenCar().updatePosition(maxX, maxY);
		playingField.repaint();
		
		
		Car drivenCar = playingField.getDrivenCar();
		for (int i = 0; i < cars.length; i++) {
			if (cars[i].isCrunched( )) {continue;}
			
			Car crashedCar = collisionStrategy.detectCollision(drivenCar, cars[i]);
			if(crashedCar != null){
				bang.play();
				crashedCar.setCrunched();
				if(drivenCar.isCrunched()){
					endGame();
					JOptionPane.showMessageDialog(null,
												  "You lost the game!",
												  "Information",
												  JOptionPane.INFORMATION_MESSAGE);
					initGame();
				}else{
					boolean playerWon = true;
					for (int z = 0; z < cars.length; z++) {
						playerWon = playerWon && cars[z].isCrunched();
						if(!playerWon){ break; }
					}
					if(playerWon){
						endGame();
						JOptionPane.showMessageDialog(null,
													  "Congratulation, You win the game!",
													  "Information",
													  JOptionPane.INFORMATION_MESSAGE);
						initGame();
					}
				}
			}
		}
	}
	
	/**
	 * @return isrunning.
	 */
	public boolean isRunning(){
		return this.isRunning;
	}
	
	/**
	 * 
	 * @return ispaused
	 */
	public boolean isPaused(){
		return this.isPaused;
	}
	
	/**
	 * @return fps
	 */
	public static int getFramesPerSecond(){
		return 1000 / sleepTime;
	}
	
	/**
	 * @param framesPerSecond as int value
	 * @throws IllegalArgumentException -
	 */
	public static void setFramesPerSecond(int framesPerSecond) throws IllegalArgumentException {
		if(framesPerSecond > 0){
			sleepTime = 1000 / framesPerSecond;
		}else{
			throw new IllegalArgumentException("Frames per second must be greater than 0.");
		}
	}
	
	private void fillInOptions() {
		setOptions(new ArrayList<BaseOption>());
		getOptions().add(new BumpersGameOption("Collision strategy", this.collisionStrategy.getName(), OptionType.DROPDOWN));
		getOptions().add(new BumpersGameOption("Test", this.test, OptionType.DROPDOWN));
	}
	
	/**
	 * @param collisionStrategy the desired collStrat
	 */
	public void setCollisionStrategy(CollisionStrategy collisionStrategy) {
		this.collisionStrategy = collisionStrategy;
	}
	
	/**
	 * @return coll strat.
	 */
	public CollisionStrategy getCollisionStrategy() {
		return collisionStrategy;
	}

	@Override
	public Boolean showOptions() throws GameException {
		System.out.println("Printing list of the selected options for Bumpers: ");
		System.out.println("================================== ");
		for (BaseOption option : getOptions()) {
			System.out.println(((BumpersGameOption) option).getOptionName() + " ("+option.getType()+"): " + ((BumpersGameOption) option).getOptionValue());
		}
		
		return Boolean.TRUE;
	}
}

